import json
import logging
from datetime import datetime
import requests

from app.config.fatigue_config import fatigue_config
from app.utils.ll_distance import haversine


class RunStatus:
    RUNNING_STATUS = 1
    STOP_STATUS = 0


class SpeedSection:
    def __init__(self):
        self.begin_point: list = None
        self.end_point: list = None
        self.run_status: int = None
        self.continue_time: int = 0
        self.total_run_time: int = 0


class IllegalDetail:
    def __init__(self):
        self.speed_sections: list = []
        self.run_times: int = 0
        self.illegal: bool = False
        self.max_rest_time: int = 0
        self.begin_rest_point: list = None
        self.end_rest_point: list = None

    def add_speed_sections(self, section):
        self.speed_sections.append(section)

    def add_run_times(self, time):
        self.run_times += time
        return self.run_times

    def init(self):
        self.run_times = 0
        self.speed_sections.clear()
        self.max_rest_time = 0
        self.illegal = False


class FatigueAnalyzer:
    def __init__(self, vehicle_name: str, track_list: list[dict]):
        self.fatigue_config = fatigue_config
        self.vehicle_name = vehicle_name
        self.simple_date_format = "%Y-%m-%d %H:%M:%S"
        self.track_list = track_list
        self.diffDistance = 2

    def call(self):
        try:
            result = self.track_list
            if not result:
                return None

            # Process points
            lists = []
            last_long = 0.0
            last_lat = 0.0
            count_num = 0
            for point in result:
                mileage = 0
                if count_num == 0:
                    mileage = 0
                else:
                    mileage = haversine(last_long, last_lat, float(point['longgitude']), float(point['latitude']))
                count_num += 1
                last_long = float(point['longgitude'])
                last_lat = float(point['latitude'])
                item = [
                    "ACC:ON",
                    point['uptime'],
                    # datetime.fromtimestamp(int(split['2']) / 1000).strftime(self.simple_date_format),
                    mileage,  # mileage
                    point['speed'],  # speed
                    f"{point['longgitude']},{point['latitude']}"  # lat,lng
                ]

                lists.append(item)

            if not self.check_file_content(lists):
                return None

            self.data_rectify(lists)
            self.filter_zero_value(lists)

            if not self.check_file_content(lists):
                return None

            jump_sections = self.jump_point(lists)
            speed_sections = self.section_filter(lists)
            illegal_detail = self.illegal_verification(speed_sections)
            self.filter_min_rest_time(illegal_detail)

            map_result = self.data_exec(illegal_detail, jump_sections)
            # 注释轨迹
            map_result["trackList"] = result

            if illegal_detail.illegal:
                details = map_result["details"]
                first_map = details[0]
                last_map = details[-1]
                start_time = first_map["startTime"]
                end_time = last_map["endTime"]

                s_time = datetime.strptime(start_time, self.simple_date_format)
                e_time = datetime.strptime(end_time, self.simple_date_format)

                wf_list = []
                for item in lists:
                    gps_time = datetime.strptime(item[1], self.simple_date_format)
                    if s_time <= gps_time <= e_time:
                        wf_list.append(item[4])

                map_result["wfList"] = wf_list
            map_result.pop("details", None)
            map_result.pop("jumpList", None)
            map_result.pop("wfList", None)

            # self.getLocation(map_result)
            return map_result

        except Exception as e:
            logging.error(f"数据处理异常:{str(e)}")
            raise Exception("Data processing error") from e

    def filter_min_rest_time(self, illegal_detail):
        if not illegal_detail.speed_sections:
            return

        speed_sections = illegal_detail.speed_sections

        if not illegal_detail.illegal:
            illegal_detail.begin_rest_point = speed_sections[0].begin_point
            illegal_detail.end_rest_point = speed_sections[-1].end_point
            return

        last_section = speed_sections[-1]
        diff_times = (datetime.strptime(str(last_section.end_point[1]), self.simple_date_format) -
                      datetime.strptime(str(last_section.begin_point[1]),
                                        self.simple_date_format)).total_seconds() * 1000

        is_clean = False
        if last_section.run_status == RunStatus.STOP_STATUS and diff_times >= self.fatigue_config.continue_rest_time:
            speed_sections.pop()
            is_clean = True

        total_rest_time = 0
        begin_point = None
        end_point = None

        for i in range(len(speed_sections)):
            run_status = speed_sections[i].run_status
            continue_time = speed_sections[i].continue_time
            rest_time = 0

            if run_status == RunStatus.STOP_STATUS:
                rest_time = continue_time

            current_continue_time = continue_time
            for j in range(i + 1, len(speed_sections)):
                if speed_sections[j].run_status == RunStatus.STOP_STATUS:
                    rest_time = max(speed_sections[j].continue_time, rest_time)

                current_continue_time += speed_sections[j].continue_time

                if current_continue_time >= self.fatigue_config.continue_run_time:
                    if total_rest_time == 0:
                        total_rest_time = rest_time
                        begin_point = speed_sections[i].begin_point
                        end_point = speed_sections[j].end_point
                    else:
                        if total_rest_time >= rest_time:
                            total_rest_time = rest_time
                            begin_point = speed_sections[i].begin_point
                            end_point = speed_sections[j].end_point
                    break

        if is_clean:
            speed_sections.append(last_section)

        illegal_detail.max_rest_time = total_rest_time
        illegal_detail.begin_rest_point = begin_point
        illegal_detail.end_rest_point = end_point

    def jump_point(self, lists):
        speed_sections = []
        for i in range(len(lists) - 1):
            self.jump_point_section(lists[i], lists[i + 1], speed_sections)
        return speed_sections

    def jump_point_section(self, current_point, next_point, speed_sections):
        diff_times = (datetime.strptime(str(next_point[1]), self.simple_date_format) -
                      datetime.strptime(str(current_point[1]), self.simple_date_format)).total_seconds() * 1000

        if diff_times >= self.fatigue_config.jump_continue_time:
            section = SpeedSection()
            section.begin_point = current_point
            section.end_point = next_point
            speed_sections.append(section)

    def data_rectify(self, lists):
        for i in range(len(lists) - 1):
            self.speed_rectify(lists[i], lists[i + 1])

        i = 0
        while i < len(lists) - 1:
            i = self.time_rectify(i, lists[i], lists[i + 1], lists)
            i += 1

    def time_rectify(self, i, current_point, next_point, lists):
        diff_times = (datetime.strptime(str(next_point[1]), self.simple_date_format) -
                      datetime.strptime(str(current_point[1]), self.simple_date_format)).total_seconds() * 1000

        flag = diff_times >= self.fatigue_config.jump_continue_time

        if flag and float(current_point[3]) > 0:
            new_point = current_point.copy()
            new_point[3] = "0.0"
            lists.insert(i + 1, new_point)
            return i + 1

        if flag and float(next_point[3]) > 0:
            new_point = next_point.copy()
            new_point[3] = "0.0"
            lists.insert(i + 1, new_point)
            return i + 1

        return i

    def speed_rectify(self, current_point, next_point):
        current_speed = float(current_point[3])
        next_speed = float(next_point[3])

        speed_check = current_speed * next_speed == 0
        gps_check = abs(float(current_point[2]) - float(next_point[2])) < self.diffDistance

        if speed_check and gps_check:
            if current_speed == 0:
                next_point[3] = "0.0"
            else:
                current_point[3] = "0.0"

    def data_exec(self, illegal_detail, jump_sections):
        result = {
            "vehicleNum": self.vehicle_name,
            "illegal": "疲劳驾驶" if illegal_detail.illegal else "未违法",
            "maxRestTime": self.format_duration(illegal_detail.max_rest_time),
            "totalRunTime": self.format_duration(illegal_detail.run_times),
            "beginTime": illegal_detail.begin_rest_point[1] if illegal_detail.begin_rest_point else "无",
            "endTime": illegal_detail.end_rest_point[1] if illegal_detail.end_rest_point else "无",
            "jumpPoint": "存在" if jump_sections else "不存在",
            "startPoint": illegal_detail.begin_rest_point[4],
            "endPoint": illegal_detail.end_rest_point[4],
            "details": [],
            "jumpList": []
        }

        if jump_sections:
            for section in jump_sections:
                result["jumpList"].append({
                    "startTime": str(section.begin_point[1]),
                    "endTime": str(section.end_point[1])
                })

        for i, section in enumerate(illegal_detail.speed_sections):
            duration = (datetime.strptime(str(section.end_point[1]), self.simple_date_format) -
                        datetime.strptime(str(section.begin_point[1]), self.simple_date_format)).total_seconds() * 1000

            result["details"].append({
                "num": i + 1,
                "startTime": section.begin_point[1],
                "startPosition": section.begin_point[4],
                "endTime": section.end_point[1],
                "endPosition": section.end_point[4],
                "continueTime": self.format_duration(duration),
                "status": "运行" if section.run_status == RunStatus.RUNNING_STATUS else "休息",
                "totalRunTime": self.format_duration(section.total_run_time)
            })

        return result

    def illegal_verification(self, speed_sections):
        illegal_detail = IllegalDetail()

        for section in speed_sections:
            illegal_detail.add_speed_sections(section)

            diff_times = (datetime.strptime(str(section.end_point[1]), self.simple_date_format) -
                          datetime.strptime(str(section.begin_point[1]),
                                            self.simple_date_format)).total_seconds() * 1000
            section.continue_time = diff_times

            if section.run_status == RunStatus.RUNNING_STATUS:
                total_run_times = illegal_detail.add_run_times(diff_times)
                section.total_run_time = total_run_times
            else:
                if diff_times >= self.fatigue_config.continue_rest_time:
                    if illegal_detail.run_times >= self.fatigue_config.continue_run_time:
                        illegal_detail.illegal = True
                        break
                    else:
                        illegal_detail.init()
                        section.total_run_time = 0
                else:
                    total_run_times = illegal_detail.add_run_times(diff_times)
                    illegal_detail.max_rest_time = max(illegal_detail.max_rest_time, diff_times)
                    section.total_run_time = total_run_times

                    if total_run_times >= self.fatigue_config.continue_run_time:
                        illegal_detail.illegal = True

        illegal_detail.illegal = illegal_detail.run_times >= self.fatigue_config.continue_run_time
        return illegal_detail

    def section_filter(self, lists):
        speed_sections = []
        section = SpeedSection()
        section.begin_point = lists[0]
        section.run_status = RunStatus.RUNNING_STATUS
        speed_sections.append(section)

        for i in range(len(lists)):
            if i == len(lists) - 1:
                self.cut_section(lists[i], None, speed_sections)
            else:
                self.cut_section(lists[i], lists[i + 1], speed_sections)

        if speed_sections and speed_sections[-1].run_status == RunStatus.STOP_STATUS:
            speed_sections.pop()

        return speed_sections

    def cut_section(self, current_point, next_point, speed_sections):
        section = speed_sections[-1]
        run_status = section.run_status

        if run_status == RunStatus.RUNNING_STATUS:
            if float(current_point[3]) == 0:
                section.end_point = current_point
                if next_point:
                    new_section = SpeedSection()
                    new_section.begin_point = current_point
                    new_section.run_status = RunStatus.STOP_STATUS
                    speed_sections.append(new_section)
            else:
                if not next_point:
                    section.end_point = current_point
        else:
            if float(current_point[3]) > 0:
                section.end_point = current_point
                if next_point:
                    new_section = SpeedSection()
                    new_section.begin_point = current_point
                    new_section.run_status = RunStatus.RUNNING_STATUS
                    speed_sections.append(new_section)
            else:
                if not next_point:
                    section.end_point = current_point

    def filter_zero_value(self, lists):
        i = 0
        while i < len(lists):
            if float(lists[i][3]) == 0.0:
                lists.pop(i)
            else:
                break

    def check_file_content(self, lists):
        if not lists:
            print(f"{self.vehicle_name}没有轨迹信息！！")
            return False
        return True

    def format_duration(self, milliseconds):
        seconds = milliseconds / 1000
        hours = int(seconds // 3600)
        minutes = int((seconds % 3600) // 60)
        seconds = int(seconds % 60)
        return f"{hours}小时{minutes}分钟{seconds}秒" if hours > 0 else f"{minutes}分钟{seconds}秒"

    def getLocation(self, map_result):
        # 发起 GET 请求，并跳过 SSL 证书验证

        param: dict = dict()
        param['key'] = fatigue_config.map_key
        param['location'] = map_result['startPoint']+";"+map_result['endPoint']
        param['inGb'] = "g84"
        param['outGb'] = "g02"
        param['road'] = "true"
        param['poi'] = "true"
        param['language'] = "ch"

        response = requests.get(fatigue_config.map_url, params=param, verify=False)
        if response.status_code == 200:
            position: dict = json.loads(response.text)
            info: list = position['data']
            area1 = info[0]['ad']['prov']+info[0]['ad']['city']+info[0]['ad']['dist']
            area2 = info[1]['ad']['prov'] + info[1]['ad']['city'] + info[1]['ad']['dist']
            map_result['startPoint'] = area1+info[0]['pois'][0]['addr']+info[0]['pois'][0]['nm']
            map_result['endPoint'] = area2+info[1]['pois'][0]['addr']+info[1]['pois'][0]['nm']
            print(info[0]['pois'][0]['addr'], info[1]['pois'][0]['addr'])
            return info[0]['pois'][0]['addr'], info[1]['pois'][0]['addr']
